﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using LitJson;
using Sliver.Network.Components;
using UnityEngine;
using WebSocketSharp;

namespace Sliver.Network.WebSocket.Client
{
    public class WebSocketClient : AbsWebSocketClient
    {
        protected WebSocketSharp.WebSocket WebSocket;

        protected WebSocketMessageDispatcher MessageDispatcher;

        // 未被确认的消息
        protected ConcurrentQueue<Tuple<MessageOut, Action>> ActionQueue = new ConcurrentQueue<Tuple<MessageOut, Action>>();
        
        protected Thread MessageThread;
        protected Thread HeartbeatThread;

        protected bool Alive = true;

        protected const int MaxActionCount = 1000;
        protected Semaphore ActionConfirmSemaphore = new Semaphore(1, 1);
        protected Semaphore ActionQueueSemaphore = new Semaphore(0, MaxActionCount);

        protected const int MaxMessageCount = 10000;
        // protected Condition SendMessageCondition = 
        // 发送队列信号量
        protected Semaphore SendMessageSemaphore = new Semaphore(0, MaxActionCount);

        protected int CurrentActionSeq = -1;
        protected Action CurrentActionCallback = null;
        
        public WebSocketClient(WebSocketMessageDispatcher messageDispatcher)
        {
            WebSocket = new WebSocketSharp.WebSocket($"ws://{NetworkManager.HostIP}:{NetworkManager.WebSocketClientPort}");
            WebSocket.OnOpen += OnOpen;
            WebSocket.OnError += OnError;
            WebSocket.OnClose += OnClose;
            WebSocket.OnMessage += OnReceiveMessage;

            MessageThread = new Thread(MethodLoop);
            MessageThread.Start();

            MessageDispatcher = messageDispatcher;
        }

        public override bool IsConnected => WebSocket.IsConnected;

        public override void Connect()
        {
            Connect(null);
        }

        public void Connect(Dictionary<string, string> headers)
        {
            if (headers != null)
            {
                WebSocket.CustomHandshakeParams = headers;
            }

            WebSocket.ConnectAsync();
        }

        public override void Close()
        {
            Alive = false;
            MessageThread?.Abort();
            HeartbeatThread?.Abort();
            WebSocket?.CloseAsync();
        }

        protected void MessageSendLoop()
        {
            while (IsConnected)
            {
                
            }
        }

        protected void MethodLoop()
        {
            while (true)
            {
                // 等待发送队列产生消息
                ActionQueueSemaphore.WaitOne();
                // 等待动作确认
                ActionConfirmSemaphore.WaitOne();
                if (!Alive) return;
                if (ActionQueue.TryDequeue(out Tuple<MessageOut, Action> message))
                {
                    CurrentActionSeq = message.Item1.id;
                    WebSocket.Send(message.Item1.ToJson());
                }
            }
        }

        protected const int PingInterval = 5000;

        /// <summary>
        /// 维持心跳
        /// </summary>
        protected void HeartbeatLoop()
        {
            while (true)
            {
                if (!Alive) return;
                WebSocket.Ping();
                // if (WebSocket.IsConnected)
                // {
                //     SendHeartbeat();
                // }
                Thread.Sleep(PingInterval);
            }
        }

        public void OnOpen(object sender, EventArgs args)
        {
            Alive = true;

            // 心跳线程
            HeartbeatThread = new Thread(HeartbeatLoop);
            HeartbeatThread.Start();

            foreach (IWebSocketClientListener listener in Listeners)
            {
                listener.OnOpen(this, args);
            }
        }

        public void OnError(object sender, ErrorEventArgs args)
        {
            Debug.Log($"websocket error: {args.Exception.StackTrace}");
            foreach (IWebSocketClientListener listener in Listeners)
            {
                listener.OnError(this, args);
            }
        }

        public void OnClose(object sender, CloseEventArgs args)
        {
            foreach (IWebSocketClientListener listener in Listeners)
            {
                listener.OnClose(this, args);
            }
        }

        public void OnReceiveMessage(object sender, MessageEventArgs args)
        {
            Debug.Log($"websocket message: {args.Data}");

            MessageIn message;
            try
            {
                message = JsonMapper.ToObject<MessageIn>(args.Data);
            }
            catch (Exception)
            {
                Debug.Log("parse json object error: " + args.Data);
                return;
            }


            foreach (IWebSocketClientListener listener in Listeners)
            {
                listener.OnReceiveMessage(this, message);
            }

            switch (message.type)
            {
                case WebSocketMessageType.OK:
                    ConfirmAction(message);
                    break;
                case WebSocketMessageType.Error:
                    Debug.Log(message.data.ToJson());
                    break;
                case WebSocketMessageType.Action:
                    // 收到action，确认消息
                    SendOK(message.id);
                    MessageDispatcher.Dispatch(message);
                    break;
                default:
                    MessageDispatcher.Dispatch(message);
                    break;
            }
        }

        protected void ConfirmAction(MessageIn message)
        {
            if (message.id != CurrentActionSeq)
            {
                Debug.Log("action 序列不一致");
                return;
            }
            
            ActionConfirmSemaphore.Release();
        }

        public override void SendService(string path, object data = null)
        {
            SendMessage(WebSocketMessageType.Service, path, data);
        }

        public override void SendInfo(string path, object data = null)
        {
            SendMessage(WebSocketMessageType.Info, path, data);
        }

        // Action 的发送严格按照序号，并且当前序号未被确认时下一个序号暂不发送
        public override void SendAction(string path, object data = null, Action callback = null)
        {
            SendMessage(WebSocketMessageType.Action, path, data, callback);
        }

        public void SendOK(int id)
        {
            // message的序列就是action的序列
            MessageOut message = new MessageOut(id, WebSocketMessageType.OK, "", null);
            WebSocket.SendAsync(message.ToJson(), (bool completed) => { });
        }

        public void SendDeny(int seq)
        {
            MessageOut message = new MessageOut(seq, WebSocketMessageType.Deny, "", null);
            WebSocket.SendAsync(message.ToJson(), (bool completed) => { });
        }

        protected static int SequenceGenerator = 1;
        protected static int ActionSequenceGenerator = 1;
        protected static int ChatSequenceGenerator = 1;

        public override void SendMessage(string type, string path, object data, Action callback = null)
        {
            if (!WebSocket.IsConnected)
            {
                Debug.Log($"send message <{path}> failed, websocket not connect");
                return;
            }

            MessageOut message;
            switch (type)
            {
                case WebSocketMessageType.Service:
                    message = new MessageOut(SequenceGenerator++, type, path, data);
                    WebSocket.SendAsync(message.ToJson(), (bool completed) => { });
                    break;
                case WebSocketMessageType.Info:
                    message = new MessageOut(SequenceGenerator++, type, path, data);
                    WebSocket.SendAsync(message.ToJson(), (bool completed) => { });
                    break;
                case WebSocketMessageType.Chat:
                    message = new MessageOut(ChatSequenceGenerator++, type, path, data);
                    WebSocket.SendAsync(message.ToJson(), (bool completed) => { });
                    break;
                case WebSocketMessageType.Action:
                    if (ActionQueue.Count >= MaxActionCount)
                    {
                        Debug.Log("action 发送队列满");
                        break;
                    }
                    message = new MessageOut(ActionSequenceGenerator++, type, path, data);
                    // 将消息加入消息队列，Action只能一条一条发
                    ActionQueue.Enqueue(new Tuple<MessageOut, Action>(message, callback));
                    ActionQueueSemaphore.Release();
                    break;
            }
        }
    }
}